/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * Created on Jan 22, 2007
 */
package cesynch.crystal.synchronizer;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

import javax.naming.directory.Attributes;
import javax.naming.directory.SearchResult;

/**
 * <p></p>
 * 
 * @author <a href="mailto:cesynch@gmail.com">cesynch</a>
 */
public class BusinessObjectsLdapSynchronizer
{
    /** Properties file name (must be located on the classpath */
    public static final String PROPERTIES_FILE_NM = "synchronizer.properties";
    /** Property key for the system property for the ssl trust store */
    public static final String KEY_TRUSTSTORE_PATH = "javax.net.ssl.trustStore";
    /** Property key for the system property for the ssl trust store password */
    public static final String KEY_TRUSTSTORE_PWD = "javax.net.ssl.trustStorePassword";
    /** Property key for the ldap user unique attribute (normally uid) */
    public static final String KEY_USER_UNIQUE_ATTR = "ldap.users.unique.attr";
    /** Default for the ldap user unique attribute */ 
    public static final String DEFAULT_USER_UNIQUE_ATTR = "uid";
    
    Directory ldap = null;
    InfoStore istore = null;
    Properties properties = null;
    String userUniqueAttr;
    
    /**
     * <p>Private constructor for utility class.</p>
     */
    private BusinessObjectsLdapSynchronizer() throws Exception
    {
        super();
        properties = new Properties();
        InputStream in = BusinessObjectsLdapSynchronizer.class.getResourceAsStream("/" + PROPERTIES_FILE_NM);
        properties.load(in);
        String trustStore = properties.getProperty(KEY_TRUSTSTORE_PATH);
        String trustStorePwd = properties.getProperty(KEY_TRUSTSTORE_PWD);
        if (null != trustStore && null != trustStorePwd)
        {
            System.setProperty(KEY_TRUSTSTORE_PATH, trustStore);
            System.setProperty(KEY_TRUSTSTORE_PWD, trustStorePwd);
        }
        userUniqueAttr = properties.getProperty(KEY_USER_UNIQUE_ATTR, DEFAULT_USER_UNIQUE_ATTR);
        ldap = new Directory(properties);
        istore = new InfoStore(properties);
    }

    /**
     * 
     * @param args
     */
    public static void main(String[] args)
    {
        try
        {
            BusinessObjectsLdapSynchronizer job = new BusinessObjectsLdapSynchronizer();
            System.out.println("Synchronizing Groups");
            job.synchGroups();
            System.out.println("Synchronizing People");
            job.synchPeople();
            System.out.println("CESynch Completed.");
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    
    public void synchPeople() throws Exception
    {
        Person currentPerson = null; 
        Set allUsers = new HashSet(); //list which contains the users
        Iterator it = istore.getPeopleIterator();
        Iterator groupIterator = null;
        Group group = null;
        
        while (it.hasNext())
        {
            currentPerson = (Person) it.next();
            SearchResult sr = ldap.findPerson(currentPerson.getUid());
            if (sr == null)
            {
                ldap.createPerson(currentPerson);
            }
            else
            {
                ldap.modifyPerson(currentPerson, sr);
            }
            
            // Synch person to group assignment
            groupIterator = currentPerson.ceGroupIdIterator();
            Set activeGroups = new HashSet();
            while (groupIterator.hasNext())
            {
                group = istore.getGroupByCeID((Integer) groupIterator.next());
                activeGroups.add(group);
                if (!ldap.groupHasMember(currentPerson, group))
                {
                    
                    // Add person to the group
                    ldap.addGroupMember(currentPerson, group);
                }
            }
            
            // remove person from obsolete group assigments
            Set ldapActiveGroups = ldap.findGroupsForPerson(currentPerson);
            ldapActiveGroups.removeAll(activeGroups);
            Iterator removeIterator = ldapActiveGroups.iterator();
            Group inactiveGroup = null;
            while(removeIterator.hasNext())
            {
                inactiveGroup = (Group) removeIterator.next();
                ldap.removeGroupMember(currentPerson, inactiveGroup);
            }
            
            // build collection of all users for "delete obsolete ldap users
            allUsers.add(currentPerson);
        }
        
        // Delete obsololete ldap users
        Enumeration people = ldap.getPeople();
        SearchResult sr = null;
        Attributes attrs = null;
        while (people.hasMoreElements())
        {
            currentPerson.reset();

            sr = (SearchResult) people.nextElement();
            attrs = sr.getAttributes();
            currentPerson.setUid((String) attrs.get(userUniqueAttr).get());
            
            if (!allUsers.contains(currentPerson))
            {
                ldap.removePerson(currentPerson);
            }
        }
     }
    
    public void synchGroups() throws Exception
    {
        Group currentGroup = null; 
        Set allGroups = new HashSet(); //list which contains the users
        Iterator it = istore.getGroupInterator();
        while (it.hasNext())
        {
            currentGroup = (Group) it.next();
            if (!ldap.groupExists(currentGroup))
            {
                ldap.createGroup(currentGroup);
            }
            
            allGroups.add(currentGroup);
        }
        
        // Delete obsololete ldap groups
        Set groups = ldap.getGroups();
        it = groups.iterator();
        while (it.hasNext())
        {
            currentGroup = (Group) it.next();
            if (!allGroups.contains(currentGroup))
            {
                ldap.removeGroup(currentGroup);
            }
        }
    }
}
